package com.dianping.cat.local;

import java.util.List;
import java.util.Objects;

import com.dianping.cat.message.Event;
import com.dianping.cat.message.Message;
import com.dianping.cat.message.Transaction;
import com.dianping.cat.message.spi.MessageTree;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

/**
 * 使用日志系统进行输出, 无法提供查找功能
 * @author LQ
 *
 */
@Slf4j
public class DefaultCatLocalMessageTreeDealer implements CatLocalMessageTreeDealer {

	/**
	 * From
	 * {@code DefaultMessageProducer.logError(String message, Throwable cause)}
	 */
	private static final String[] EXCEPTION_TYPES = new String[] { "Error", "RuntimeException", "Exception" };

	@Override
	public void deal(MessageTree tree) {
		final SearchResult sr = new SearchResult();
		search(tree, sr);
		if (sr.isHasException()) {
			log.error("{}", tree);
		} else {
			log.debug("{}", tree);
		}
	}

	protected void search(MessageTree tree, SearchResult sr) {
		final Message message = tree.getMessage();
		loopMessage(message, sr);
	}

	protected void loopMessage(Message message, SearchResult sr) {
		if (message instanceof Transaction) {
			Transaction transaction = (Transaction) message;
			List<Message> children = transaction.getChildren();

			if (children.isEmpty()) {
				sr.setDurationInMicro(durationInMicros(transaction, 'A', Policy.WITH_DURATION));
			} else {
				int len = children.size();

				for (int i = 0; i < len; i++) {
					Message child = children.get(i);
					if (child != null) {
						loopMessage(child, sr);
					}
				}

				sr.setDurationInMicro(durationInMicros(transaction, 'T', Policy.WITH_DURATION));
			}
		} else if (message instanceof Event) {
			String eventType = ((Event) message).getType();
			if (arrayIndexOf(EXCEPTION_TYPES, eventType) > -1) {
				sr.setHasException(true);
			}
		}
	}

	private <T> int arrayIndexOf(T[] array, Object value) {
		for (int i = 0; i < array.length; i++) {
			if (Objects.equals(value, array[i])) {
				return i;
			}
		}
		return -1;
	}

	/**
	 * 
	 * @param message
	 * @param policy
	 * @return 单位为 us
	 */
	long durationInMicros(Message message, char type, Policy policy) {
		if (policy != Policy.WITHOUT_STATUS) {
			if (policy == Policy.WITH_DURATION && message instanceof Transaction) {
				// "us"
				return ((Transaction) message).getDurationInMicros();
			}
		}

		return -1;
	}

	// ===============================================
	enum Policy {
		DEFAULT,

		WITHOUT_STATUS,

		WITH_DURATION;

		public static Policy getByMessageIdentifier(byte identifier) {
			switch (identifier) {
			case 't':
				return WITHOUT_STATUS;
			case 'T':
			case 'A':
				return WITH_DURATION;
			case 'E':
			case 'H':
				return DEFAULT;
			default:
				return DEFAULT;
			}
		}
	}

	@Data
	protected static class SearchResult {
		private boolean hasException;

		private long durationInMicro = -1;

		private String traceId;
	}
}